/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.engine.core;

import io.iec.edp.caf.boot.context.CAFContext;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public class BefEngineInfoCache {

    //  private static Map<String, BefEngineInfo> maps = new HashMap<>(1000);
    //BEId和租户的映射关系 key为beId value为租户信息，较少
    private static ConcurrentHashMap<String, CopyOnWriteArrayList<BETenantInfo>> tenantBEMap = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<Integer, ConcurrentHashMap<String, BefEngineInfo>> tenantMap = new ConcurrentHashMap<>();

    public static BefEngineInfo getBefEngineInfo(String beId) {
        int tenantId = CAFContext.current.getTenantId();
        if (tenantMap.containsKey(tenantId)) {
            ConcurrentHashMap<String, BefEngineInfo> befEngineInfoMap = tenantMap.get(tenantId);
            if (befEngineInfoMap.containsKey(beId))
                return befEngineInfoMap.get(beId);
            synchronized (befEngineInfoMap) {
                if (befEngineInfoMap.containsKey(beId))
                    return befEngineInfoMap.get(beId);
                BefEngineInfo info = new BefEngineInfo();
                befEngineInfoMap.put(beId, info);
                return info;
            }
        }
        //没有版本的，直接new就行
        ConcurrentHashMap<String, BefEngineInfo> befEngineInfoMap = new ConcurrentHashMap<>();
        BefEngineInfo info = new BefEngineInfo();
        befEngineInfoMap.put(beId, info);
        tenantMap.put(tenantId, befEngineInfoMap);
        return info;
    }

    public static BefEngineInfo getBefEngineInfo(String beId, String version) {
        int tenantId = CAFContext.current.getTenantId();
        BefEngineInfo befEngineInfo = getBefEngineInfoFromCache(beId, version);
        if (befEngineInfo != null)
            return befEngineInfo;

        BETenantInfo beTenantInfo = new BETenantInfo();
        beTenantInfo.setVersion(version);
        beTenantInfo.setTenantId(tenantId);
        if (tenantBEMap.containsKey(beId)) {
            tenantBEMap.get(beId).add(beTenantInfo);
        } else {
            CopyOnWriteArrayList<BETenantInfo> list = new CopyOnWriteArrayList<>();
            list.add(beTenantInfo);
            tenantBEMap.put(beId, list);
        }
        BefEngineInfo info = new BefEngineInfo();
        info.setCacheVersion(version);
        if (!tenantMap.containsKey(tenantId)) {
            tenantMap.put(tenantId, new ConcurrentHashMap<>());
        }
        tenantMap.get(tenantId).put(beId, info);
        return info;
    }

    //从缓存中获取
    private static BefEngineInfo getBefEngineInfoFromCache(String beId, String version) {
        int tenantId = CAFContext.current.getTenantId();
        if (!tenantBEMap.containsKey(beId)) {
            return null;
        }
        //先查当前租户信息
        CopyOnWriteArrayList<BETenantInfo> copyOnWriteArrayList = tenantBEMap.get(beId);
        if (copyOnWriteArrayList == null)
            return null;
        for (BETenantInfo beTenantInfo : copyOnWriteArrayList) {
            //原来使用的ArrayList，多线程下会有null的问题
            if (beTenantInfo == null || beTenantInfo.getTenantId() != tenantId)
                continue;
            if (!beTenantInfo.getVersion().equalsIgnoreCase(version)) {
                //版本不一致，缓存失效。。删除
                copyOnWriteArrayList.remove(beTenantInfo);
                if (tenantMap.containsKey(tenantId) && tenantMap.get(tenantId).containsKey(beId)) {
                    tenantMap.get(tenantId).remove(beId);
                }
                break;
            }
            return tenantMap.get(tenantId).get(beId);
        }

        //查询其他租户对应BE的版本和传入版本是否一致，如果一致，作为当前租户的BefEngineInfo返回，避免重新查询(版本使用时间戳，排除不一致问题)
        for (BETenantInfo beTenantInfo : copyOnWriteArrayList) {
            //查询版本与传入版本相同的
            if (beTenantInfo == null || !beTenantInfo.getVersion().equalsIgnoreCase(version)) {
                continue;
            }
            BefEngineInfo befEngineInfo = tenantMap.get(beTenantInfo.getTenantId()).get(beId);
            if (befEngineInfo != null && befEngineInfo.getCacheVersion().equalsIgnoreCase(version))
                return befEngineInfo;
            else {
                //容错处理，缓冲中版本不一致，删除（tenantMap中的没有删除）
                if (befEngineInfo != null) {
                    tenantMap.get(beTenantInfo.getTenantId()).remove(beId);
                }
                copyOnWriteArrayList.remove(beTenantInfo);
                break;
            }
        }
        return null;
    }

    public static void removeBefEngineInfo(String beid) {
        //只删除跟当前租户相关的
        int tenantId = CAFContext.current.getTenantId();
        if (tenantBEMap.containsKey(beid)) {
            CopyOnWriteArrayList<BETenantInfo> copyOnWriteArrayList = tenantBEMap.get(beid);
            if (copyOnWriteArrayList != null) {
                for (BETenantInfo beTenantInfo : copyOnWriteArrayList) {
                    if (beTenantInfo.getTenantId() == tenantId) {
                        copyOnWriteArrayList.remove(beTenantInfo);
                        break;
                    }
                }
            }
        }
        if (tenantMap.containsKey(tenantId)) {
            if (tenantMap.get(tenantId).containsKey(beid)) {
                tenantMap.get(tenantId).remove(beid);
            }
        }
    }

    public static void removeAll() {
        int tenantId = CAFContext.current.getTenantId();
        if (tenantMap.containsKey(tenantId)) {
            tenantMap.remove(tenantId);
        }
        tenantBEMap.clear();
    }

}
